package utility;

import java.awt.Image;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import gui.AST_array;
import gui.AST_colon;
import gui.AST_comma;
import gui.AST_do;
import gui.AST_dot;
import gui.AST_elif;
import gui.AST_else;
import gui.AST_endfbk;
import gui.AST_endfwd;
import gui.AST_endtyp;
import gui.AST_endvar;
import gui.AST_equal;
import gui.AST_fblock;
import gui.AST_for;
import gui.AST_forward;
import gui.AST_fraction;
import gui.AST_func;
import gui.AST_id;
import gui.AST_if;
import gui.AST_int;
import gui.AST_lbrace;
import gui.AST_lbracket;
import gui.AST_logic_and;
import gui.AST_logic_not;
import gui.AST_logic_or;
import gui.AST_lparen;
import gui.AST_minus;
import gui.AST_natural;
import gui.AST_plus;
import gui.AST_rbrace;
import gui.AST_rbracket;
import gui.AST_real;
import gui.AST_relation;
import gui.AST_return;
import gui.AST_rparen;
import gui.AST_semicolon;
import gui.AST_slash;
import gui.AST_star;
import gui.AST_string;
import gui.AST_struct;
import gui.AST_tChar;
import gui.AST_tString;
import gui.AST_to;
import gui.AST_type;
import gui.AST_variable;
import gui.AST_while;
import gui.ASTargList;
import gui.ASTarrayElement;
import gui.ASTassign;
import gui.ASTblock;
import gui.ASTcomparison;
import gui.ASTcomparison_onepart;
import gui.ASTcomparison_single;
import gui.ASTcondition;
import gui.ASTcondition_firstPart;
import gui.ASTdeclaration;
import gui.ASTdeclarationBlock;
import gui.ASTexpression;
import gui.ASTfactor;
import gui.ASTfunctionBlock;
import gui.ASTfunctionCall;
import gui.ASTfunctionForDec;
import gui.ASTfunctionForDecBlock;
import gui.ASTfunctionHead;
import gui.ASTfunctionImpl;
import gui.ASTlex;
import gui.ASTlogicals;
import gui.ASTloop;
import gui.ASTmember;
import gui.ASToperators;
import gui.ASTothers;
import gui.ASTprogram;
import gui.ASTrelations;
import gui.ASTreservedWords;
import gui.ASTreturn_;
import gui.ASTseparators;
import gui.ASTslice;
import gui.ASTstatement;
import gui.ASTstructDec;
import gui.ASTsubCondition;
import gui.ASTterm;
import gui.ASTtype;
import gui.ASTtypeDecBlock;
import gui.GlowwormVisitor;
import gui.ParseException;
import gui.SimpleNode;
import gui.Token;

public class GlowwormDumpVisitor implements GlowwormVisitor {

    private SymbolTable symTable;
    private SymbolTable structTable;
    private String declare;
    private String assign;
    private double state;
    // private String rightType;
    private String expressionHelp;
    private String factorHelp;
    private String memberHelp;
    private String exprType;
    private Boolean assignJ = false;// determine where token is array
    private Boolean dotJ = false;// in checkExpression
    private FuncAttributes assignFaH = null;
    private String returnHelp = "";// Save return id's type
    private String returnHelp1 = "";// Save return type in functionHead
    private Vector<String> errorMsg;
    private Vector<String> declareMsg;
    private String structHelp = "";

    private String helphelp = "var";
    private Integer funcHeadHelp = 0;
    private Boolean assignHelp = false;// false mean isDeclared locally
    private int inFor = 0;
    private Map<String, Token> map = new HashMap<String, Token>();
    private Map<String, Token> funMap = new HashMap<String, Token>();

    // private int scopeHelp = 0;

    // 0 typeDecBlock()
    // 1 declarationBlock()
    // 2 functionForDecBlock()
    // 3 functionBlock()
    // 4 block()
    void error(String msg, String name, String lineMsg, String columnMsg) {
        errorMsg.add("Problematic variable: " + name);
        errorMsg.add(lineMsg);
        errorMsg.add(columnMsg);
        errorMsg.add(msg);
    }

    private Boolean decStruct(Token token) {

        token = token.next.next;
        if (structTable.declaredLocally(token.image, "var") != null) {
            error("The struct is already declared", token.image, ""
                    + token.beginLine, "" + token.beginColumn);
            return false;
        }
        String structName = token.image;
        token = token.next.next;
        StructAttributes sa = new StructAttributes();
        Here: while (true) {
            // in case it's a array
            Token tempToken = token.next.next;
            if (token.image.equals("}"))
                break;
            if (tempToken.image.equals("array")) {
                ArrayAttributes aa = new ArrayAttributes(true);
                aa.setBounds(Integer.parseInt(tempToken.next.next.image));
                String sEleType = token.image;

                switch (Type.getType(sEleType)) {
                case ID:
                    StructAttributes sta = (StructAttributes) structTable
                            .declaredLocally(token.image, "var");
                    if (sta == null) {
                        error("The struct is not declared", token.image, ""
                                + token.beginLine, "" + token.beginColumn);
                        while (token != null
                                && (!token.image.equals(",") || !token.image
                                        .equals("}")))
                            token = token.next;
                        if (token.image.equals("}"))
                            break Here;
                        break;
                    }
                }

                aa.setEleType(token.image);
                String argName = tempToken.next.next.next.next.next.image;
                sa.addArg(aa, argName);
                token = tempToken.next.next.next.next.next.next;
                if (token.equals(","))
                    token = token.next;
                continue;
            }

            switch (Type.getType(token.image)) {
            case INT:
                sa.addArg(new IntegerAttributes(true), tempToken.image);
                break;
            case REAL:
                sa.addArg(new RealAttributes(true), tempToken.image);
                break;
            case STRING:
                sa.addArg(new StringAttributes(true), tempToken.image);
                break;
            default:
                // id
                StructAttributes sta = (StructAttributes) structTable
                        .declaredLocally(token.image, "var");
                if (sta == null) {
                    error("The struct is not declared", token.image, ""
                            + token.beginLine, "" + token.beginColumn);
                    break;
                }
                sa.addArg(sta, tempToken.image);
                break;
            }
            token = tempToken.next;
            if (token.image.equals(","))
                token = token.next;
        }
        structTable.entrySymbol(structName, sa);
        return true;

    }

    private String checkExpressionB(Token token, String typeR, String end) {
        String typeA = "";
        while (token != null) {

            String tempType = getType(token, typeR);

            if (tempType.equals("ok"))// temporary ok
            {
                token = token.next;
                if (token.image.equals(end))
                    return "over";
                continue;
            }

            if (tempType.equals("no"))
                return "no";// the id usage is not ok
            else if (tempType.equals("false")) {
                error("The expression assignment is not compatible",
                        token.image, "" + token.beginLine, ""
                                + token.beginColumn);
                return "false";// means function assignment is not ok
            } else {
                typeA = tempType;
                if (typeR.equals(""))
                    typeR = typeA;

            }

            Token ttt = token;
            if (assignJ) {

                while (ttt != null && !ttt.image.equals("]"))
                    ttt = ttt.next;

            }

            if (ttt.next.image.equals(end)) {
                assignJ = false;
                return "over";
            }
            if (typeA.equals("")) {
                continue;// meet with '('
            }

            if ((typeA.equals("num") && !(typeR.equals("real") || typeR
                    .equals("int"))) || !typeA.equals(typeR)) {
                error("The expression assignment is not compatible",
                        token.image, "" + token.beginLine, ""
                                + token.beginColumn);
                return "false";
            }
            if (assignJ) {
                while (token != null && !token.image.equals("]"))
                    token = token.next;
                assignJ = false;
            }
            token = token.next;
        }

        if (end.equals(""))
            return "over";
        System.out.println("error in checkExpressionB");
        return typeA;
    }

    private String checkExpression(Token token, String typeR, String end) {
        String typeA = "";
        while (token != null) {
            token = token.next;
            if (token.image.equals(",") && !dotJ)
                return "dot";// come with dot
            else if (token.image.equals(",") && dotJ) {
                dotJ = false;
                return "ok";
            } else
                dotJ = true;
            String tempType = getType(token, typeR);

            if (tempType.equals("ok"))// temporary ok
                continue;

            if (tempType.equals("no"))
                return "no";// the id usage is not ok
            else if (tempType.equals("false")) {
                error("The function argument is not compatible", token.image,
                        "" + token.beginLine, "" + token.beginColumn);
                return "false";// means function assignment is not ok
            } else {
                typeA = tempType;
            }

            if (typeA.equals("")) {
                if (token.next.image.equals(end))
                    return "over";
                continue;// meet with '('
            }

            if ((typeA.equals("num") && !(typeR.equals("real") || typeR
                    .equals("int"))) || !typeA.equals(typeR)) {
                error("The function argument is not compatible", token.image,
                        "" + token.beginLine, "" + token.beginColumn);
                return "false";
            }
            if (assignJ) {
                while (token != null && !token.image.equals("]"))
                    token = token.next;
                assignJ = false;
            }
        }
        System.out.println("error in checkExpression");
        return typeA;
    }

    private Boolean checkArg(Token token, FuncAttributes fa) {
        Token tokenT = token.next;// '('
        String type = "";
        int i = 0;
        int maxI = fa.getArgType().size();
        Vector<Attributes> v = fa.getArgType();
        while (tokenT != null) {

            if (i != 0 && (i > maxI - 1)) {
                error("The number of funcArg is not compatible with function declaration",
                        token.image, "" + token.beginLine, ""
                                + token.beginColumn);
                return false;
            }
            String see = v.get(i).getType();
            if (see.equals("array")) {
                ArrayAttributes bb = (ArrayAttributes) (v.get(i));
                see += " " + bb.getEleType();
            }
            String ce = checkExpression(tokenT, see, ";");
            tokenT = tokenT.next;
            if (ce.equals("dot")) {
                error("The argument is missing", "no argument", ""
                        + tokenT.beginLine, "" + tokenT.beginColumn);
                return false;
            } else if (ce.equals("no"))
                return false;
            else if (ce.equals("false"))
                return false;
            else if (ce.equals("ok")) {
                while (tokenT != null && !tokenT.image.equals(","))
                    tokenT = tokenT.next;
            } else if (ce.equals("over")) {
                if (i != maxI - 1) {
                    error("The number of funcArg is not compatible with function declaration",
                            token.image, "" + token.beginLine, ""
                                    + token.beginColumn);
                    return false;
                } else
                    return true;
            }

            i++;
        }
        System.out.println("error in checkArg");
        return true;
    }

    private FuncAttributes checkFunctionCall(Token token) {
        if (state == 4)
            helphelp = "func";

        String funName = token.image;
        FuncAttributes fa = (FuncAttributes) isDeclared(funName, "func");
        assignFaH = fa;
        if (state == 4)
            helphelp = "var";
        if (fa == null) {
            error("The function is not declared", funName,
                    "" + token.beginLine, "" + token.beginColumn);
            return null;
        }

        else {
            if (checkArg(token, fa))
                return fa;
            else {
                return null;
            }
        }
    }

    private String getType(Token token, String rightType) {
        char firstC = token.image.charAt(0);
        switch (firstC) {
        case '-':
        case '+':
        case '\\':
        case '*':
            if (rightType.equals(""))
                return "num";// todo
            else if (!(rightType.equals("int") || rightType.equals("real") || !rightType
                    .equals("num")))
                return "false";
            break;
        case '\'':
            if (rightType.equals(""))
                return "char";
            else if (!(rightType.equals("char")))
                return "false";
            break;
        case '\"':
            if (rightType.equals(""))
                return "string";
            else if (!(rightType.equals("string")))
                return "false";
            break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            String tempType = "";
            if (token.image.indexOf(".") == -1)
                tempType = "int";
            else
                tempType = "real";

            if (rightType.equals("") || rightType.equals("num"))
                return tempType;
            else if (!(rightType.equals(tempType)))
                return "false";
            break;
        case '(':
        case ')':
            // todo
            return "";

        default:
            Token tempTo = token.next;
            Attributes aaT = isDeclared(token.image, "var");
            if (aaT == null) {
                error("The variable is not declared", token.image, ""
                        + token.beginLine, "" + token.beginColumn);
                return "no";
            }
            if (tempTo.image.equals("[")) {
                if (!aaT.getType().equals("array")) {
                    error("The variable is not compatible with its usage",
                            token.image, "" + token.beginLine, ""
                                    + token.beginColumn);
                    return "no";
                }
                ArrayAttributes bb = (ArrayAttributes) aaT;

                tempTo = tempTo.next;
                if (tempTo.image.charAt(0) <= '9'
                        && tempTo.image.charAt(0) >= '0') {
                    if (Integer.parseInt(tempTo.image) > bb.getBounds() - 1
                            || Integer.parseInt(tempTo.image) < 0) {
                        error("The array is out of bound", token.image, ""
                                + token.beginLine, "" + token.beginColumn);
                        return "no";
                    }
                } else {
                    Attributes aaTT = isDeclared(tempTo.image, "var");
                    if (aaTT == null) {
                        error("The variable is not declared", tempTo.image, ""
                                + tempTo.beginLine, "" + tempTo.beginColumn);
                        return "no";
                    }
                    if (!aaTT.getType().equals("int")) {
                        error("The variable is not a natural", tempTo.image, ""
                                + tempTo.beginLine, "" + tempTo.beginColumn);
                        return "no";
                    }

                    if (rightType.equals("")) {
                        assignJ = true;
                        return bb.getEleType();
                    } else if (!(rightType.equals(bb.getEleType()))) {
                        return "false";
                    }
                    assignJ = true;
                }
            } else {
                Attributes aa = isDeclared(token.image, "var");
                if (aa == null) {
                    error("The variable is not declared", token.image, ""
                            + token.beginLine, "" + token.beginColumn);
                    return "no";
                } else if (rightType.equals("")) {
                    if (aa.getType().equals("array")) {
                        ArrayAttributes bb = (ArrayAttributes) aa;
                        return bb.getType() + " " + bb.getEleType();
                    } else {
                        return aa.getType();
                    }
                } else if (aa.getType().equals("array")) {
                    ArrayAttributes bb = (ArrayAttributes) aa;
                    if (!rightType.equals(bb.getType() + " " + bb.getEleType()))
                        return "false";

                } else {
                    if (!aa.getType().equals(rightType))
                        return "false";
                }

                break;
            }

        }
        return "ok";
    }

    private Attributes isDeclared(String name, String kind) {
        if (assignHelp) {
            SymbolEntry se = (SymbolEntry) symTable.getScopeDisplay().get(0);
            while (se != null
                    && (!se.getName().equals(name) || !se.getAttributes()
                            .getKind().equals(helphelp)))
                se = se.getLevel();
            if (se == null)
                return null;
            return se.getAttributes();
        }

        Attributes aa = symTable.declaredLocally(name, kind);
        if (aa != null)
            return aa;
        SymbolEntry se = symTable.retrieveSymbol(name);
        while (se != null && se.getDepth() != 0)
            se = se.getHash();
        if (se == null)
            return null;
        return se.getAttributes();
    }

    private Boolean isCompatible(String type1, String type2) {
        if (type1.equals(type2))
            return true;
        return false;
    }

    public SymbolTable getSymTable() {
        return symTable;
    }

    public GlowwormDumpVisitor() {
        symTable = new SymbolTable();
        structTable = new SymbolTable();
        declare = "";
        state = -1;
        errorMsg = new Vector<String>();
        declareMsg = new Vector<String>();
        assign = "";
        expressionHelp = "";
        factorHelp = "";
    }

    public Vector<String> getErrorMsg() {
        return errorMsg;
    }

    public Vector<String> getDeclareMsg() {
        return declareMsg;
    }

    private void setDelcareMsg(String name, String type, int line, int column) {
        declareMsg.add(name);
        declareMsg.add(type);
        declareMsg.add(Integer.toString(line));
        declareMsg.add(Integer.toString(column));
    }

    // Origin
    public Object visit(SimpleNode node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTlex node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTreservedWords node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTrelations node, Object data) {
        System.out.println("No declaration found.");
        return data;

    }

    public Object visit(ASTseparators node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASToperators node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTothers node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTlogicals node, Object data) {
        System.out.println("No declaration found.");
        return data;
    }

    public Object visit(ASTfactor node, Object data) {
        factorHelp = "";
        data = node.childrenAccept(this, data);

        return data;
    }

    public Object visit(ASTterm node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTexpression node, Object data) {
        if (state == 4)
            helphelp = "var";
        exprType = "";
        expressionHelp = "";
        data = node.childrenAccept(this, data);
        factorHelp += exprType;

        if (inFor != 0) {
            if (inFor == 2) {
                checkExpressionB(node.jjtGetFirstToken(), "int", "to");
                inFor--;
            } else if (inFor == 1) {
                checkExpressionB(node.jjtGetFirstToken(), "int", "do");
                inFor--;
            }

        }
        return data;
    }

    public Object visit(ASTcomparison_single node, Object data) {
        data = node.childrenAccept(this, data);
        Token token = node.jjtGetFirstToken();
        String end = "";
        while (token != null) {
            String ss = token.image;
            if (ss.equals("==") || ss.equals(">") || ss.equals("<")
                    || ss.equals("<=") || ss.equals(">=") || ss.equals("!=")) {
                end = ss;
                token = token.next;
                break;
            }
            token = token.next;
        }
        checkExpressionB(node.jjtGetFirstToken(), "", end);
        checkExpressionB(token, "", ")");
        // return data;

        return data;
    }

    public Object visit(ASTcomparison_onepart node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTcomparison node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTarrayElement node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTmember node, Object data) {
        memberHelp = "";
        data = node.childrenAccept(this, data);
        factorHelp += memberHelp;
        return data;
    }

    public Object visit(ASTtype node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTargList node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTstructDec node, Object data) {
        structHelp = "";
        data = node.childrenAccept(this, data);
        System.out.println(structHelp);
        Token token = node.jjtGetFirstToken();
        decStruct(token);
        // while (token != null && !token.image.equals(""))
        // token = token.next;
        return data;
    }

    public Object visit(ASTstatement node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTassign node, Object data) {
        // assignJ=false;
        assign = "";
        data = node.childrenAccept(this, data);

        // System.out.println(assign);
        // System.out.println("-------------------------");
        // Token tokenDDD = node.jjtGetFirstToken();
        // while (tokenDDD != null && !tokenDDD.image.equals(";"))
        // {
        // System.out.print(tokenDDD.image+" ");
        // tokenDDD = tokenDDD.next;
        // }
        // System.out.println("\n******************");
        // data =null;
        // if(data == null)
        // return data;

        String[] assignTemp = assign.split(" ");
        String leftA = assignTemp[0];
        Attributes aa = isDeclared(leftA, "var");
        String leftType = "";
        String rightType = "";

        Token token = node.jjtGetFirstToken();
        while (token != null && !(token.image.equals(leftA)))
            token = token.next;

        if (aa == null) {
            error("The variable is not declared", leftA, "" + token.beginLine,
                    "" + token.beginColumn);
            return data;
        }
        if (assignTemp[1].equals("[")) {
            ArrayAttributes bb = (ArrayAttributes) aa;
            leftType = bb.getEleType();
            char tempC = assignTemp[2].charAt(0);
            if (tempC <= '9' && tempC >= '0') {
                int tempN = Integer.parseInt(assignTemp[2]);

                int originN = bb.getBounds();
                if (tempN > originN - 1 || tempN < 0) {
                    error("The array is out of bound", leftA, ""
                            + token.beginLine, "" + token.beginColumn);
                    return data;
                }
            } else {

                Attributes aaTT1 = isDeclared(assignTemp[2], "var");
                if (aaTT1 == null) {
                    error("The variable is not declared", assignTemp[2], ""
                            + token.beginLine, "" + token.beginColumn + 2);
                    return data;
                }
                if (!aaTT1.getType().equals("int")) {
                    error("The variable is not a natural", assignTemp[2], ""
                            + token.beginLine, "" + token.beginColumn + 2);
                    return data;
                }

            }
        } else if (token.next.image.equals(".")) {
            StructAttributes sa = (StructAttributes) aa;

            while (true) {
                token = token.next.next;
                Vector<String> argName = sa.getArgName();
                Vector<Attributes> argType = sa.getArgType();
                int i = 0;
                for (; i < argName.size(); i++) {
                    if (token.image.equals(argName.get(i)))
                        break;
                }

                if (i == argName.size()) {
                    error("The struct member is not exist", token.image, ""
                            + token.beginLine, "" + token.beginColumn);
                    return data;
                }

                if (argType.get(i).getType().equals("int")
                        || argType.get(i).getType().equals("string")
                        || argType.get(i).getType().equals("real")) {
                    leftType = argType.get(i).getType();
                    break;
                } else {
                    if (!token.next.image.equals(".")) {
                        error("LHS cannot be struct", token.image, ""
                                + token.beginLine, "" + token.beginColumn);
                        return data;
                    }
                    aa = structTable.declaredLocally(argType.get(i).getType(),
                            "var");
                }
            }

        } else {
            leftType = aa.getType();

            if (leftType.equals("array")) {
                ArrayAttributes see = (ArrayAttributes) aa;
                leftType += " " + see.getEleType();
            }

        }

        while (token != null && !(token.image.equals("=")))
            token = token.next;
        if (token != null) {
            Token tt = token.next;
            if (tt.next != null && tt.next.image.equals("(")) {
                // FuncAttributes fa = checkFunctionCall(tt);
                FuncAttributes fa = assignFaH;

                if (fa == null)
                    return data;
                if (leftType.equals(fa.getReturnAtt().getType()))
                    return data;
                else {
                    error("The assignment is not compatible", tt.image, ""
                            + tt.beginLine, "" + tt.beginColumn);
                    return data;
                }

            } else if (tt.next != null && tt.next.image.equals(".")) {

                Token tt1 = tt.next.next;
                StructAttributes sa = (StructAttributes) isDeclared(tt.image,
                        "var");

                if (sa == null) {
                    error("The variable is not declared", tt.image, ""
                            + tt.beginLine, "" + tt.beginColumn);
                    return data;
                }

                Vector<String> argName = sa.getArgName();
                Vector<Attributes> argType = sa.getArgType();
                int i = 0;
                for (; i < argName.size(); i++) {
                    if (tt1.image.equals(argName.get(i)))
                        break;
                }

                if (i == argName.size()) {
                    error("The struct member is not exist", token.image, ""
                            + token.beginLine, "" + token.beginColumn);
                    return data;
                }

                if (!leftType.equals(argType.get(i).getType())) {
                    error("The assignment is not compatible", tt.image, ""
                            + tt.beginLine, "" + tt.beginColumn);
                }
                return data;
            }
        }

        while (token != null) {
            token = token.next;
            if (token.image.equals(";"))
                return data;
            String tempType = getType(token, rightType);

            if (tempType.equals("ok")) {
                if (assignJ) {
                    while (token != null && !token.image.equals("]"))
                        token = token.next;
                    assignJ = false;
                }
                continue;
            }

            if (tempType.equals("no"))
                return data;
            else if (tempType.equals("false")) {
                error("The assignment is not compatible", token.image, ""
                        + token.beginLine, "" + token.beginColumn);
                return data;
            }
            // else if (tempType.equals("num")) {
            // tempType = rightType;
            // Token tempT = token.next;
            // String tt = getType(tempT, rightType);
            //
            // if (tempType.equals("no"))
            // return data;
            // else if (tempType.equals("false")) {
            // error("The assignment is not compatible", token.image, ""
            // + token.beginLine, "" + token.beginColumn);
            // return data;
            // } else {
            // if (tempType.equals("")) {
            // rightType = tt;
            // } else if (!tempType.equals(tt)) {
            // error("The assignment is not compatible", token.image,
            // "" + token.beginLine, "" + token.beginColumn);
            // return data;
            // }
            // }
            // }
            else {
                if (rightType.equals(""))
                    rightType = tempType;
            }

            if (rightType.equals(""))
                continue;

            if ((rightType.equals("num") && !(leftType.equals("real") || leftType
                    .equals("int"))) || !rightType.equals(leftType)) {
                error("The assignment is not compatible", token.image, ""
                        + token.beginLine, "" + token.beginColumn);
                return data;
            }
            if (assignJ) {
                while (token != null && !token.image.equals("]"))
                    token = token.next;
                assignJ = false;
            }
            /*
             * char firstC = token.image.charAt(0); switch (firstC) { case '-':
             * if (rightType.equals("")) rightType = "num";// todo break; case
             * '\'': rightType = "char"; break; case '\"': rightType = "string";
             * break; case '0': case '1': case '2': case '3': case '4': case
             * '5': case '6': case '7': case '8': case '9': if
             * (token.image.indexOf(".") != -1) rightType = "int"; else
             * rightType = "real"; break; case '(': // todo break; default:
             * Token tempTo = token.next; Attributes aaT =
             * isDeclared(token.image, "var"); if (aaT == null) {
             * error("The variable is not declared", token.image, "" +
             * token.beginLine, "" + token.beginColumn); return data; } if
             * (tempTo.image.equals("[")) { if (!aaT.getType().equals("array"))
             * { error("The variable is not compatible with its usage",
             * token.image, "" + token.beginLine, "" + token.beginColumn);
             * return data; } ArrayAttributes bb = (ArrayAttributes) aaT; if
             * (rightType.equals("")) rightType = bb.getEleType(); else if
             * (!(rightType.equals(bb.getEleType()))) {
             * error("The assignment is not compatible", token.image, "" +
             * token.beginLine, "" + token.beginColumn); return data; }
             * 
             * tempTo = tempTo.next; if (tempTo.image.charAt(0) <= '9' &&
             * tempTo.image.charAt(0) >= '9') { if
             * (Integer.parseInt(tempTo.image) > bb.getBounds() - 1 ||
             * Integer.parseInt(tempTo.image) < 0) {
             * error("The array is out of bound", token.image, "" +
             * token.beginLine, "" + token.beginColumn); return data; } } else {
             * Attributes aaTT = isDeclared(tempTo.image, "var"); if (aaTT ==
             * null) { error("The variable is not declared", tempTo.image, "" +
             * tempTo.beginLine, "" + tempTo.beginColumn); return data; } if
             * (!aaTT.getType().equals("int")) {
             * error("The variable is not a natural", tempTo.image, "" +
             * tempTo.beginLine, "" + tempTo.beginColumn); } }
             * 
             * }
             * 
             * }
             */
        }

        if (!leftType.equals(rightType))
            error("The assignment is not compatible", token.image, ""
                    + token.beginLine, "" + token.beginColumn);
        return data;
    }

    public Object visit(ASTdeclaration node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTloop node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTcondition node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTcondition_firstPart node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTsubCondition node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTfunctionCall node, Object data) {
        data = node.childrenAccept(this, data);
        Token token = node.jjtGetFirstToken();
        checkFunctionCall(token);
        return data;
    }

    public Object visit(ASTfunctionHead node, Object data) {

        declare = "";
        data = node.childrenAccept(this, data);

        if (declare.equals(""))
            return data;
        if (!(state == 3 || state == 2))
            return data;

        if (state == 2)
            symTable.openScope();

        map.clear();
        // System.out.println(declare);
        String[] tempDe = declare.split(" ");
        int count = 0;
        String symName = "";
        for (int i = 0; i < tempDe.length; i++) {
            if (tempDe[i].charAt(0) == '*') {
                if (count != 0) {

                }
                symName = tempDe[i].substring(1);
                count++;
                FuncAttributes fa = new FuncAttributes();
                if (tempDe[i - 2].equals("]")) {

                    ArrayAttributes aa = new ArrayAttributes();
                    aa.setEleType(tempDe[i - 7]);
                    aa.setBounds(Integer.parseInt(tempDe[i - 3]));
                    fa.setReturnAtt(aa);
                } else {
                    String type = tempDe[i - 2];
                    switch (Type.getType(type)) {
                    case INT:
                        fa.setReturnAtt(new IntegerAttributes());
                        break;
                    case REAL:
                        fa.setReturnAtt(new RealAttributes());
                        break;
                    case STRING:
                        fa.setReturnAtt(new StringAttributes());
                        break;
                    }

                    if (symTable.declaredLocally(symName, "func") != null) {
                        Token token = map.get(symName).next;
                        while (token != null && !(token.image.equals(symName)))
                            token = token.next;
                        map.put(symName, token);
                        error("This func is already declared", symName, ""
                                + token.beginLine, "" + +token.beginColumn);
                        ErrorAttributes symIa = new ErrorAttributes();// Here
                                                                      // Problem
                    } else {
                        Token token = node.jjtGetFirstToken();
                        Token lastToken = node.jjtGetLastToken();
                        while (token != null && !(token.image.equals(symName)))
                            token = token.next;
                        map.put(symName, token);

                        funMap.clear();
                        for (int j = i + 1; j < tempDe.length; j++) {
                            // **********

                            if (tempDe[j].charAt(0) == '*') {
                                String symNameF = tempDe[j].substring(1);
                                if (symTable.declaredLocally(symNameF, "var") != null) {
                                    Token tokenF = funMap.get(symNameF).next;
                                    while (tokenF != null
                                            && !(tokenF.image.equals(symNameF)))
                                        tokenF = tokenF.next;
                                    funMap.put(symNameF, tokenF);
                                    error("This variable is already declared:",
                                            symNameF, "" + tokenF.beginLine, ""
                                                    + tokenF.beginColumn);
                                    ErrorAttributes symIaF = new ErrorAttributes();// Here
                                                                                   // Problem
                                } else {
                                    Token tokenF = token.next;
                                    while (tokenF != null
                                            && !(tokenF.image.equals(symNameF)))
                                        tokenF = tokenF.next;
                                    funMap.put(symNameF, tokenF);

                                    if (tempDe[j - 2].equals("]")) {

                                        ArrayAttributes aaF = new ArrayAttributes();
                                        aaF.setEleType(tempDe[j - 7]);
                                        aaF.setBounds(Integer
                                                .parseInt(tempDe[j - 3]));
                                        aaF.setParameter(true);
                                        symTable.entrySymbol(symNameF, aaF);
                                        fa.addArg(aaF, symNameF);
                                        String delType = tempDe[j - 7]
                                                + " array";
                                        setDelcareMsg(symNameF, delType,
                                                token.beginLine,
                                                token.endColumn);
                                    } else {
                                        String typeF = tempDe[j - 2];
                                        switch (Type.getType(typeF)) {
                                        case INT:
                                            symTable.entrySymbol(symNameF,
                                                    new IntegerAttributes(true));
                                            fa.addArg(new IntegerAttributes(
                                                    true), symNameF);

                                            setDelcareMsg(symNameF, "int",
                                                    tokenF.beginLine,
                                                    tokenF.endColumn);
                                            break;
                                        case REAL:
                                            symTable.entrySymbol(symNameF,
                                                    new RealAttributes(true));
                                            fa.addArg(new RealAttributes(true),
                                                    symNameF);
                                            setDelcareMsg(symNameF, "real",
                                                    tokenF.beginLine,
                                                    tokenF.endColumn);
                                            break;
                                        case STRING:
                                            symTable.entrySymbol(symNameF,
                                                    new StringAttributes(true));
                                            fa.addArg(
                                                    new StringAttributes(true),
                                                    symNameF);

                                            setDelcareMsg(symNameF, "string",
                                                    tokenF.beginLine,
                                                    tokenF.endColumn);
                                            break;

                                        }

                                    }

                                }
                            }
                            // ***********
                        }
                        if (state == 2)
                            fa.setForDec(true);
                        funcHeadHelp++;
                        fa.setDepth(funcHeadHelp);
                        symTable.entrySymbol(symName, fa);

                        returnHelp1 = "";
                        returnHelp1 += fa.getReturnAtt().getType();
                        if (returnHelp1.equals("array")) {
                            ArrayAttributes bb = (ArrayAttributes) fa
                                    .getReturnAtt();
                            returnHelp1 += " " + bb.getEleType();
                        }

                        break;
                    }
                }

            }

        }
        return data;
    }

    public Object visit(ASTfunctionImpl node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTfunctionForDec node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTslice node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTblock node, Object data) {
        if (state == -1) {
            state = 4;
            assignHelp = true;
        }
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTtypeDecBlock node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTdeclarationBlock node, Object data) {
        declare = "";
        data = node.childrenAccept(this, data);

        if (declare.equals(""))
            return data;
        // if (state == 3)
        // symTable.openScope();

        map.clear();
        if (state == 3)
            map.putAll(funMap);

        // if (state == -1)
        // state = 1;
        String[] tempDe = declare.split(" ");
        for (int i = 0; i < tempDe.length; i++) {
            if (tempDe[i].charAt(0) == '*') {
                String symName = tempDe[i].substring(1);
                if ((i + 1) != tempDe.length && tempDe[i + 1].equals(":"))
                    continue;
                if (symTable.declaredLocally(symName, "var") != null) {
                    Token token = map.get(symName).next;
                    while (token != null && !(token.image.equals(symName)))
                        token = token.next;
                    map.put(symName, token);
                    error("This variable is already declared:", symName, ""
                            + token.beginLine, "" + token.beginColumn);
                    ErrorAttributes symIa = new ErrorAttributes();// Here
                                                                  // Problem
                } else {
                    Token token = node.jjtGetFirstToken();
                    while (token != null && !(token.image.equals(symName)))
                        token = token.next;
                    map.put(symName, token);

                    if (i - 2 >= 0 && tempDe[i - 2].equals("]")) {

                        ArrayAttributes aa = new ArrayAttributes();

                        String sEleType = tempDe[i - 7];

                        switch (Type.getType(sEleType)) {
                        case ID:
                            StructAttributes sta = (StructAttributes) structTable
                                    .declaredLocally(sEleType, "var");
                            if (sta == null) {
                                error("The struct is not declared", sEleType,
                                        "" + token.beginLine, ""
                                                + (token.beginColumn - 3));
                                continue;
                            }
                        }

                        aa.setEleType(tempDe[i - 7]);
                        aa.setBounds(Integer.parseInt(tempDe[i - 3]));
                        symTable.entrySymbol(symName, aa);
                        String type = tempDe[i - 7] + " array";
                        setDelcareMsg(symName, type, token.beginLine,
                                token.endColumn);

                    } else {
                        String type = tempDe[i - 2];
                        switch (Type.getType(type)) {
                        case INT:
                            symTable.entrySymbol(symName,
                                    new IntegerAttributes());
                            setDelcareMsg(symName, "int", token.beginLine,
                                    token.endColumn);
                            break;

                        case REAL:
                            symTable.entrySymbol(symName, new RealAttributes());
                            setDelcareMsg(symName, "real", token.beginLine,
                                    token.endColumn);
                            break;
                        case STRING:
                            symTable.entrySymbol(symName,
                                    new StringAttributes());
                            setDelcareMsg(symName, "string", token.beginLine,
                                    token.endColumn);
                            break;

                        case ID:
                            StructAttributes sta = (StructAttributes) structTable
                                    .declaredLocally(type, "var");
                            if (sta == null) {
                                error("The struct is not declared", type, ""
                                        + token.beginLine, ""
                                        + (token.beginColumn - 3));
                                continue;
                            }

                            symTable.entrySymbol(symName, sta);
                            setDelcareMsg(symName, "struct", token.beginLine,
                                    token.endColumn);
                            break;

                        }

                    }

                }
            }

        }
        return data;
    }

    public Object visit(ASTfunctionForDecBlock node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTfunctionBlock node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(ASTprogram node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_struct node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_for node, Object data) {
        inFor = 2;
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_to node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_do node, Object data) {
        data = node.childrenAccept(this, data);
        inFor = 0;
        return data;
    }

    public Object visit(AST_while node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_if node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_else node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_elif node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_func node, Object data) {
        if (state == 3)
            symTable.openScope();
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_type node, Object data) {
        data = node.childrenAccept(this, data);
        state = 0;
        return data;
    }

    public Object visit(AST_endtyp node, Object data) {
        data = node.childrenAccept(this, data);
        state = -1;
        return data;
    }

    public Object visit(AST_variable node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_endvar node, Object data) {
        data = node.childrenAccept(this, data);
        // if (state != 3)
        // state = -1;
        return data;
    }

    public Object visit(AST_forward node, Object data) {
        data = node.childrenAccept(this, data);
        state = 2;
        return data;
    }

    public Object visit(AST_endfwd node, Object data) {
        data = node.childrenAccept(this, data);
        state = -1;
        return data;
    }

    public Object visit(AST_fblock node, Object data) {
        data = node.childrenAccept(this, data);
        state = 3;
        return data;
    }

    public Object visit(AST_endfbk node, Object data) {
        data = node.childrenAccept(this, data);
        state = -1;
        return data;
    }

    public Object visit(AST_int node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_real node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_string node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_array node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_relation node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_logic_and node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_logic_or node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_logic_not node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_lparen node, Object data) {
        data = node.childrenAccept(this, data);
        factorHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_lbrace node, Object data) {
        data = node.childrenAccept(this, data);
        // if (state == 3)
        // scopeHelp++;
        factorHelp += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_rparen node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_rbrace node, Object data) {
        data = node.childrenAccept(this, data);
        // if (state == 3) {
        // scopeHelp--;
        // if (scopeHelp == 0)
        // symTable.closeScope();
        // }
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_lbracket node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        assign += node.name + " ";
        factorHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_rbracket node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        assign += node.name + " ";
        factorHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_semicolon node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_colon node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        return data;
    }

    public Object visit(AST_comma node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_dot node, Object data) {
        data = node.childrenAccept(this, data);
        memberHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_plus node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_minus node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }

    public Object visit(AST_star node, Object data) {
        data = node.childrenAccept(this, data);
        // assign += node.name + " ";
        return data;
    }

    public Object visit(AST_slash node, Object data) {
        data = node.childrenAccept(this, data);
        // assign += node.name + " ";
        return data;
    }

    public Object visit(AST_natural node, Object data) {
        data = node.childrenAccept(this, data);
        declare += node.name + " ";
        assign += node.name + " ";
        factorHelp += node.name + " ";
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_equal node, Object data) {
        data = node.childrenAccept(this, data);
        assign += node.name + " ";
        return data;
    }

    public Object visit(AST_id node, Object data) {
        data = node.childrenAccept(this, data);
        if (!node.jjtGetFirstToken().next.image.equals(":"))
            declare += "*" + node.name + " ";
        else
            declare += node.name + " ";
        assign += node.name + " ";
        factorHelp += node.name + " ";
        memberHelp += node.name + " ";
        returnHelp += node.name;
        structHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_fraction node, Object data) {
        data = node.childrenAccept(this, data);
        factorHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_tString node, Object data) {
        data = node.childrenAccept(this, data);
        factorHelp += node.name + " ";
        return data;
    }

    public Object visit(AST_tChar node, Object data) {
        data = node.childrenAccept(this, data);
        factorHelp += node.name + " ";
        return data;
    }

    @Override
    public Object visit(ASTreturn_ node, Object data) {
        returnHelp = "";
        data = node.childrenAccept(this, data);
        Attributes aa = isDeclared(returnHelp, "var");
        Token token = node.jjtGetFirstToken();
        String reType = aa.getType();
        while (token != null && !token.image.equals(returnHelp))
            token = token.next;
        if (aa == null)
            error("The variable is not declared", token.image, ""
                    + token.beginLine, "" + token.beginColumn);
        else if (aa.getType().equals("array")) {
            ArrayAttributes bb = (ArrayAttributes) aa;
            reType += " " + bb.getEleType();
        }

        if (!returnHelp1.equals(reType))
            error("The return type is not compatible", token.image, ""
                    + token.beginLine, "" + token.beginColumn);

        return data;
    }

    @Override
    public Object visit(AST_return node, Object data) {
        data = node.childrenAccept(this, data);
        return data;
    }
}
